package com.qf.business.coupon.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.business.coupon.input.RedEnvelopesInput;
import com.qf.business.coupon.service.*;
import com.qf.common.core.utils.UserInfo;
import com.qf.common.redis.utils.LockUtils;
import com.qf.data.entity.auth.vo.WxUserVo;
import com.qf.data.entity.coupon.CouponUser;
import com.qf.data.entity.coupon.RedEnvelopes;
import com.qf.data.entity.coupon.RedEnvelopesDetails;
import com.qf.data.entity.coupon.RedEnvelopesShare;
import com.qf.data.mapper.coupon.RedEnvelopesDao;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 红包表(RedEnvelopes)表服务实现类
 *
 * @author makejava
 * @since 2021-12-06 14:49:56
 */
@Service
@Slf4j
public class RedEnvelopesServiceImpl extends ServiceImpl<RedEnvelopesDao, RedEnvelopes> implements RedEnvelopesService {

    @Autowired
    private CouponService couponService;

    @Autowired
    private CouponUserService couponUserService;

    @Autowired
    private RedEnvelopesDetailsService redEnvelopesDetailsService;

    @Autowired
    private RedEnvelopesShareService redEnvelopesShareService;

    @Autowired
    private Executor executor;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 发红包的lua脚本
     */
    private String sendRedLua = "--发红包的lua脚本\n" +
            "--接收红包的id号\n" +
            "local redid = tonumber(KEYS[1] or -1)\n" +
            "--接收红包的券数量\n" +
            "local number = tonumber(ARGV[1] or -1)\n" +
            "--接收红包的份额\n" +
            "local count = tonumber(ARGV[2] or -1)\n" +
            "--接收红包的类型\n" +
            "local type = tonumber(ARGV[3] or 0)\n" +
            "--过期时间\n" +
            "local timeout = tonumber(ARGV[4] or -1)\n" +
            "--校验参数\n" +
            "if redid == -1 or number == -1 or count == -1 then\n" +
            "   -- 参数不正确\n" +
            "   return -1\n" +
            "end \n" +
            "--生成的红包的key\n" +
            "local key = \"red_envelopes_\"..redid\n" +
            "--判断红包是否已经存在\n" +
            "local flag = redis.call('exists', key);\n" +
            "if flag == 1 then \n" +
            "    --当前id的红包已经存在\n" +
            "    return -2\n" +
            "end\n" +
            "--插入红包信息\n" +
            "redis.call('hset', key, 'number', number)\n" +
            "redis.call('hset', key, 'count', count)\n" +
            "redis.call('hset', key, 'type', type)\n" +
            "--设置过期时间\n" +
            "if timeout ~= -1 then\n" +
            "    redis.call('expire', key, timeout)\n" +
            "end\n" +
            "return 1";

    /**
     * 抢红包的lua脚本
     * -1 红包信息不存在
     * -2 红包已经抢完
     * -3 当前用户已经抢过该红包
     */
    private String robRedLua = "--获取当前的红包id\n" +
            "local redid = tonumber(KEYS[1] or -1)\n" +
            "--获取当前的用户id\n" +
            "local uid = tonumber(ARGV[1] or -1)\n" +
            "--生成的红包的key\n" +
            "local key = \"red_envelopes_\"..redid\n" +
            "--判断红包是否存在\n" +
            "local flag = redis.call('exists', key);\n" +
            "if flag == 0 then \n" +
            "    --红包不存在 根本没这个红包 或者 红包已经过期\n" +
            "    return -1\n" +
            "end\n" +
            "--判断当前红包是否已经抢完\n" +
            "local count = tonumber(redis.call(\"hget\", key, \"count\"));\n" +
            "if count == 0 then\n" +
            "    --红包已经抢完了\n" +
            "    return -2\n" +
            "end\n" +
            "--判断当前用户是否抢过该红包\n" +
            "local rt = redis.call(\"hexists\", key, \"user_\"..uid);\n" +
            "if rt == 1 then\n" +
            "     --当前用户已经抢过该红包\n" +
            "     return -3\n" +
            "end\n" +
            "--开始抢红包\n" +
            "--获取红包中剩余的优惠券数量\n" +
            "local number = tonumber(redis.call(\"hget\", key, \"number\"));\n" +
            "--获取红包类型\n" +
            "local type = tonumber(redis.call(\"hget\", key, \"type\"));\n" +
            "--抢到的优惠券的数量\n" +
            "local robNumber = 0;\n" +
            "--判断红包类型\n" +
            "if type == 0 then\n" +
            "     --固定红包\n" +
            "     robNumber =  number / count\n" +
            "else\n" +
            "     -- 随机红包\n" +
            "     if count == 1 then\n" +
            "           --只剩最后一个名额\n" +
            "           robNumber = number\n" +
            "     else \n" +
            "           --设置lua的随机种子\n" +
            "           local time = redis.call('time')\n" +
            "           local seed = time[1] + time[2]\n" +
            "           math.randomseed(seed) \t\n" +
            "           --计算随机红包\n" +
            "           robNumber = math.random(1, number / count * 2 - 1)\n" +
            "     end\n" +
            "end\n" +
            "--修改红包的数据\n" +
            "redis.call('hset', key, 'number', number - robNumber)\n" +
            "redis.call('hset', key, 'count', count - 1)\n" +
            "redis.call('hset', key, 'user_'..uid, robNumber)\n" +
            "--返回抢到的券数量\n" +
            "return robNumber ";

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 发红包
     *
     * 1、查询当前用户 拥有的 可用优惠券列表 500
     * 2、修改对应的优惠券领取记录的状态 100张 状态 0 -> 1
     * 3、生成红包记录
     * 4、对应的领取记录 添加到 红包明细
     *
     * 3W张 - 40s
     *
     * @param params
     * @return
     */
    @Override
    @Transactional
    public Long sendRedEnve(RedEnvelopesInput params) {

        //获取当前用户信息
        WxUserVo wxUserVo = UserInfo.getUser();

        //红包的类型
        Integer redType = params.getRedType();
        //红包的份额
        Integer redCount = params.getRedCount();
        //优惠券的数量
        Integer couponNumber = params.getCouponNumber();

        //查询需要发送的优惠券列表(可用的)
//        List<Coupon> coupons = couponService.userCouponList(0);
//        List<CouponUser> couponUsers = couponUserService.query()
//                .eq("u_id", wxUserVo.getWuId())
//                .eq("status", 0)
//                .orderByAsc("create_time")
//                .list();
//
//        //判断当前用户是否拥有足够的优惠券信息
//        Assert.isTrue(couponNumber <= couponUsers.size(), "没有充足的优惠券！");
//
//        //获取前N个领取记录
//        couponUsers = couponUsers.stream()
//                .limit(couponNumber).collect(Collectors.toList());

        //查询需要发送的优惠券列表(可用的)
        List<CouponUser> couponUsers = couponUserService.queryCouponUserByUidAndLimit(wxUserVo.getWuId(), couponNumber);

        //判断当前用户是否拥有足够的优惠券信息
        Assert.isTrue(couponNumber <= couponUsers.size(), "没有充足的优惠券！");

        //修改对应优惠券领取的记录状态（未使用 -> 已经使用）
        List<Long> couUserId = couponUsers.stream()
                .map(couponUser -> couponUser.getCuId())
                .collect(Collectors.toList());

        //修改对应的优惠券领取状态
        int result = couponUserService.updateCouponUserStatus(couUserId, 1);
        //乐观锁
        Assert.isTrue(result == couUserId.size(), "红包发送异常！");

        //生成红包过期时间（24小时之后）
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR_OF_DAY, 24);

        //生成红包
        RedEnvelopes redEnvelopes = new RedEnvelopes()
                .setCount(redCount)//红包的份额
                .setCouponNumber(couponNumber)//红包中优惠券的数量
                .setSendUid(wxUserVo.getWuId())//红包发送者的id
                .setType(redType)//红包的类型
                .setTimeOut(calendar.getTime());//红包的过期时间
        super.save(redEnvelopes);

        //红包id
        Long redId = redEnvelopes.getId();

        //对应放入红包明细表 -- 同步
        if (couponUsers.size() <= 1000) {
            //券的数量较小 -- 同步
            List<RedEnvelopesDetails> details = new ArrayList<>();
            for (CouponUser couponUser : couponUsers) {
                //生成明细记录
                RedEnvelopesDetails redEnvelopesDetails = new RedEnvelopesDetails()
                        .setRedId(redId)//红包id
                        .setCuId(couponUser.getCuId())//领取id
                        .setCpId(couponUser.getCpId());//券id
                details.add(redEnvelopesDetails);
            }
            redEnvelopesDetailsService.insertBatch(details);
            //同步的保存到redis中
            redisTemplate.opsForList().rightPushAll("red_envelopes_details_" + redId, details);
            redisTemplate.expire("red_envelopes_details_" + redId, 24, TimeUnit.HOURS);
        } else {
            //异步
            int threadNumber = 5;

            //计算每个线程要处理的数据量
            final int size = couponUsers.size() / threadNumber;

            //创建任务
            for (int i = 1; i <= threadNumber; i++) {
                //
                final int index = i;

                //交给线程池执行任务
                executor.execute(() -> {
                    //当前线程要处理的范围
                    // 1线程 - 0~size
                    // 2线程 - size ~ 2size
                    // 3线程 - 2size ~ 3size
                    int begin = (index - 1) * size;
                    int end = index == threadNumber ? couponUsers.size() : begin + size;

                    //获取当前线程需要处理的集合
                    List<CouponUser> threadCouponUsers = couponUsers.subList(begin, end);

                    //
                    List<RedEnvelopesDetails> details = new ArrayList<>();
                    for (CouponUser couponUser : threadCouponUsers) {
                        //生成明细记录
                        RedEnvelopesDetails redEnvelopesDetails = new RedEnvelopesDetails()
                                .setRedId(redId)//红包id
                                .setCuId(couponUser.getCuId())//领取id
                                .setCpId(couponUser.getCpId());//券id
                        details.add(redEnvelopesDetails);
                    }
                    redEnvelopesDetailsService.insertBatch(details);
                    //同步的保存到redis中
                    redisTemplate.opsForList().rightPushAll("red_envelopes_details_" + redId, details);
                    redisTemplate.expire("red_envelopes_details_" + redId, 24, TimeUnit.HOURS);
                });
            }
        }

        //执行lua脚本 - 同步红包的数据到redis中
        Long luaResult = stringRedisTemplate.execute(
                new DefaultRedisScript<>(sendRedLua, Long.class),
                Collections.singletonList(redId + ""),
                couponNumber + "", redCount + "", redType + "", 24 * 60 * 60 + "");

        Assert.isTrue(luaResult == 1, "红包信息同步到redis中异常！");

        //发布rabbitMQ
        rabbitTemplate.convertAndSend("coupon-exchange", "", redId);

        return redId;
    }

    /**
     * 抢红包
     *
     * 1、通过红包id 获取红包信息（拥有的券数量、拥有多少份）
     * 2、判断红包的状态（是否可抢）
     * 3、判断当前用户是否抢过（份额表）
     *
     * 4、判断红包类型（固定、随机）
     * 5、根据红包类型计算抢到的券数量
     * 6、修改对应红包的数据库的数据（券数量、份额）
     * 7、生成红包份额记录
     * 8、修改红包明细表记录（相关的明细记录 和 份额信息 关联）
     * 9、将抢到的券信息保存到当前用户领取关联表中（当前用户就可以使用这张券了）
     * 10、返回结果 > 0 表示抢到的券数量  < 0
     *      -1 表示当前红包已经抢完
     *      -2 当前用户已经抢过
     *
     * @return
     */
    @Override
    @Transactional
    public int robRedEnve(Long redId) {

        //获取用户信息
        WxUserVo user = UserInfo.getUser();

        //执行lua脚本 当前用户是否能够抢到红包
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<>(robRedLua, Long.class),
                Collections.singletonList(redId + ""),
                user.getWuId() + "");

        if (result < 0) {
            return result.intValue();
        }

        try {

            //当前用户已经抢到红包
            //生成份额记录
            RedEnvelopesShare redEnvelopesShare = new RedEnvelopesShare()
                    .setRedId(redId)//红包id
                    .setGetCouponNumber(result.intValue())//抢到的券数量
                    .setGetUid(user.getWuId());//抢到的用户id
            boolean save = redEnvelopesShareService.save(redEnvelopesShare);
            log.info("[rob-red] - 生成红包份额记录：{}", save);

            //添加分布式锁，保证红包明细获取的原子性
            LockUtils.lock("lock_red_" + redId);
            List<RedEnvelopesDetails> details = null;
            try {
                //从redis中获取红包明细信息 - 原子性（我取了 其他人就不能再取了）
                details = redisTemplate.opsForList().range("red_envelopes_details_" + redId, 0, result - 1);
                //删除掉当前取出的红包明细
                redisTemplate.opsForList().trim("red_envelopes_details_" + redId, result, -1);
            } finally {
                LockUtils.unlock();
            }

            //统一修改红包明细的状态
            List<Long> redDetailIds = details.stream()
                    .map(redEnvelopesDetails1 -> redEnvelopesDetails1.getId())
                    .collect(Collectors.toList());
            //修改这些红包明细的份额id
            int result2 = redEnvelopesDetailsService.updateRedDetailShareId(redDetailIds, redEnvelopesShare.getId());
            log.info("[rob-red] - 批量修改红包明细，设置到当前份额下：{}", result2);

            //循环红包明细 生成对应的红包领取记录 - 表示当前用户已经抢到这些优惠券了
            final List<RedEnvelopesDetails> detailsF = details;
            //写一个日志 -- detailsF（0）  detailsF（xxx） 人工补偿
            executor.execute(() -> {
                List<CouponUser> couponUsers = new ArrayList<>();
                for (RedEnvelopesDetails redEnvelopesDetail : detailsF) {
                    //生成对应的领取记录
                    CouponUser couponUser = new CouponUser()
                            .setUId(user.getWuId())//用户id
                            .setCpId(redEnvelopesDetail.getCpId())//券id
                            .setNumber(1);//券数量
                    couponUsers.add(couponUser);
                }
                couponUserService.insertBatch(couponUsers);
                log.info("[rob-red] - 循环生成用户优惠券领取信息");
            });
        } catch (Exception e) {
            //实现人工补偿的脚本
            //红包id  用户id
            throw e;
        }

        return result.intValue();

//        Integer robNumberRt = LockUtils.transactionLock("red_" + redId)
//                .start()
//                .exec(() -> {
//                    //抢到的数量
//                    Integer robNumber = 0;
//                    //获取用户信息
//                    WxUserVo user = UserInfo.getUser();
//
//                    //通过id查询红包记录
//                    RedEnvelopes redEnvelopes = super.getById(redId);
//                    log.info("[rob-red] - 查询红包信息 - {}", redEnvelopes);
//                    Assert.notNull(redEnvelopes, "红包信息有误！");
//
//                    //判断红包状态
//                    Integer count = redEnvelopes.getCount();
//                    if (count == 0) {
//                        //红包已经抢完
//                        log.info("[rob-red] - 红包已经抢完！");
//                        return -1;
//                    }
//
//                    //判断红包是否已经抢过
//                    Integer cou = redEnvelopesShareService.query()
//                            .eq("red_id", redEnvelopes.getId())
//                            .eq("get_uid", user.getWuId())
//                            .count();
//
//                    if (cou == 1) {
//                        //红包已经抢过
//                        log.info("[rob-red] - 当前用户已经抢过该红包！");
//                        return -2;
//                    }
//
//                    //判断红包类型
//                    log.info("[rob-red] - 开始计算红包的抢的券的数量");
//                    Integer type = redEnvelopes.getType();
//                    if (type == 0) {
//                        //固定红包
//                        robNumber = redEnvelopes.getCouponNumber() / redEnvelopes.getCount();
//                    } else if (type == 1) {
//                        //随机红包 - 随机算法
//                        if (redEnvelopes.getCount() == 1) {
//                            //最后一个人抢，直接将剩余的券返回
//                            robNumber = redEnvelopes.getCouponNumber();
//                        } else {
//                            //二倍均值法
//                            // 100张券  10个抢
//                            // （1 ~ 100 / 10 * 2） 第一个人： 1~20  10张
//                            // (1 ~ 90/9 * 2) 第二个： 1~20 10张
//                            robNumber = (int) (Math.random() * (redEnvelopes.getCouponNumber() / redEnvelopes.getCount() * 2)) + 1;
//                        }
//                    }
//                    log.info("[rob-red] - 获得本次抢的券的数量：" + robNumber);
//
//                    //修改数据库
//                    redEnvelopes.setCouponNumber(redEnvelopes.getCouponNumber() - robNumber);
//                    redEnvelopes.setCount(redEnvelopes.getCount() - 1);
//                    boolean result = super.updateById(redEnvelopes);
//                    log.info("[rob-red] - 修改数据库中红包的相关数量信息：{}", result);
//
//                    //生成份额记录
//                    RedEnvelopesShare redEnvelopesShare = new RedEnvelopesShare()
//                            .setRedId(redEnvelopes.getId())//红包id
//                            .setGetCouponNumber(robNumber)//抢到的券数量
//                            .setGetUid(user.getWuId());//抢到的用户id
//                    boolean save = redEnvelopesShareService.save(redEnvelopesShare);
//                    log.info("[rob-red] - 生成红包份额记录：{}", save);
//
//                    //查询红包明细表，获取可用的券明细，设置为当前的份额id，表示这些券 被当前这个人抢走了
//                    List<RedEnvelopesDetails> redEnvelopesDetails = redEnvelopesDetailsService.queryRedEnvelopesDetails(redEnvelopes.getId(), robNumber);
//                    //统一修改红包明细的状态
//                    List<Long> redDetailIds = redEnvelopesDetails.stream()
//                            .map(redEnvelopesDetails1 -> redEnvelopesDetails1.getId())
//                            .collect(Collectors.toList());
//                    //修改这些红包明细的份额id
//                    int result2 = redEnvelopesDetailsService.updateRedDetailShareId(redDetailIds, redEnvelopesShare.getId());
//                    log.info("[rob-red] - 批量修改红包明细，设置到当前份额下：{}", result2);
//
//
//                    //循环明细
//                    for (RedEnvelopesDetails redEnvelopesDetail : redEnvelopesDetails) {
//                        //生成对应的领取记录
//                        CouponUser couponUser = new CouponUser()
//                                .setUId(user.getWuId())//用户id
//                                .setCpId(redEnvelopesDetail.getCpId())//券id
//                                .setNumber(1);//券数量
//                        couponUserService.save(couponUser);
//                    }
//                    log.info("[rob-red] - 循环生成用户优惠券领取信息");
//
//                    return robNumber;
//                });
//
//        return robNumberRt;

//-------------------------------------------------
//        LockUtils.lock("red_" + redId);
//        try {
//            //获取用户信息
//            WxUserVo user = UserInfo.getUser();
//
//            //通过id查询红包记录
//            RedEnvelopes redEnvelopes = super.getById(redId);
//            log.info("[rob-red] - 查询红包信息 - {}", redEnvelopes);
//            Assert.notNull(redEnvelopes, "红包信息有误！");
//
//            //判断红包状态
//            Integer count = redEnvelopes.getCount();
//            if (count == 0) {
//                //红包已经抢完
//                log.info("[rob-red] - 红包已经抢完！");
//                return -1;
//            }
//
//            //判断红包是否已经抢过
//            Integer cou = redEnvelopesShareService.query()
//                    .eq("red_id", redEnvelopes.getId())
//                    .eq("get_uid", user.getWuId())
//                    .count();
//
//            if (cou == 1) {
//                //红包已经抢过
//                log.info("[rob-red] - 当前用户已经抢过该红包！");
//                return -2;
//            }
//
//            //判断红包类型
//            log.info("[rob-red] - 开始计算红包的抢的券的数量");
//            Integer type = redEnvelopes.getType();
//            if (type == 0) {
//                //固定红包
//                robNumber = redEnvelopes.getCouponNumber() / redEnvelopes.getCount();
//            } else if (type == 1) {
//                //随机红包 - 随机算法
//                if (redEnvelopes.getCount() == 1) {
//                    //最后一个人抢，直接将剩余的券返回
//                    robNumber = redEnvelopes.getCouponNumber();
//                } else {
//                    //二倍均值法
//                    // 100张券  10个抢
//                    // （1 ~ 100 / 10 * 2） 第一个人： 1~20  10张
//                    // (1 ~ 90/9 * 2) 第二个： 1~20 10张
//                    robNumber = (int) (Math.random() * (redEnvelopes.getCouponNumber() / redEnvelopes.getCount() * 2)) + 1;
//                }
//            }
//            log.info("[rob-red] - 获得本次抢的券的数量：" + robNumber);
//
//            //修改数据库
//            redEnvelopes.setCouponNumber(redEnvelopes.getCouponNumber() - robNumber);
//            redEnvelopes.setCount(redEnvelopes.getCount() - 1);
//            boolean result = super.updateById(redEnvelopes);
//            log.info("[rob-red] - 修改数据库中红包的相关数量信息：{}", result);
//
//            //生成份额记录
//            RedEnvelopesShare redEnvelopesShare = new RedEnvelopesShare()
//                    .setRedId(redEnvelopes.getId())//红包id
//                    .setGetCouponNumber(robNumber)//抢到的券数量
//                    .setGetUid(user.getWuId());//抢到的用户id
//            boolean save = redEnvelopesShareService.save(redEnvelopesShare);
//            log.info("[rob-red] - 生成红包份额记录：{}", save);
//
//            //查询红包明细表，获取可用的券明细，设置为当前的份额id，表示这些券 被当前这个人抢走了
//            List<RedEnvelopesDetails> redEnvelopesDetails = redEnvelopesDetailsService.queryRedEnvelopesDetails(redEnvelopes.getId(), robNumber);
//            //统一修改红包明细的状态
//            List<Long> redDetailIds = redEnvelopesDetails.stream()
//                    .map(redEnvelopesDetails1 -> redEnvelopesDetails1.getId())
//                    .collect(Collectors.toList());
//            //修改这些红包明细的份额id
//            int result2 = redEnvelopesDetailsService.updateRedDetailShareId(redDetailIds, redEnvelopesShare.getId());
//            log.info("[rob-red] - 批量修改红包明细，设置到当前份额下：{}", result2);
//
//
//            //循环明细
//            for (RedEnvelopesDetails redEnvelopesDetail : redEnvelopesDetails) {
//                //生成对应的领取记录
//                CouponUser couponUser = new CouponUser()
//                        .setUId(user.getWuId())//用户id
//                        .setCpId(redEnvelopesDetail.getCpId())//券id
//                        .setNumber(1);//券数量
//                couponUserService.save(couponUser);
//            }
//            log.info("[rob-red] - 循环生成用户优惠券领取信息");
//        } catch (Exception e) {
//           log.error("[rob-red] - 抢红包的异常！", e);
//           throw e;
//        } finally {
//            LockUtils.unlock();
//        }
//        return robNumber;
    }

    /**
     * 通过xxl-job 每10分钟触发一次
     * @return

    @Transactional
    public int reBack(){

        //查询所有过期的红包 10
        List<RedEnvelopes> redEnvelopes = service.query(xxx);
        for (RedEnvelopes redEnvelope : redEnvelopes) {
            //依次处理redEnvelope的回退业务 9个 1个
            try {
                //设置一个回滚点
                获取当前的代理对象.robXxxRed();
            }catch (Exception e) {

            }

        }

        return 0;
    }

    @Transactional(propagation = Propagation.NESTED)
    public int robXxxRed(RedEnvelopes redEnvelopes){

    }
     */

    /**
     * 处理红包回退 - 延迟队列
     * @param redId
     * @return
     */
    @Override
    public int redBack(Long redId) {

        Integer result = LockUtils.transactionLock("lock_red_" + redId)
                .start()
                .exec(() -> {

                    //查询红包状态
                    RedEnvelopes redEnvelopes = super.getById(redId);

                    //判断状态
                    if (redEnvelopes.getStatus() == 1) {
                        //红包已经抢完
                        return 0;
                    }

                    //查询redis,获取所有还未领取的红包明细
                    List<RedEnvelopesDetails> details = redisTemplate.opsForList().range("red_envelopes_details_" + redId, 0, -1);

                    //修改这些明细的状态
                    List<Long> deIds = details.stream().map(redEnvelopesDetails -> redEnvelopesDetails.getId()).collect(Collectors.toList());
                    //修改红包明细的份额id 为-1 表示不能再抢了
                    redEnvelopesDetailsService.updateRedDetailShareId(deIds, -1L);

                    //修改红包的状态
                    redEnvelopes.setStatus(2);//已经过期
                    RedEnvelopesServiceImpl.this.updateById(redEnvelopes);

                    //生成一个红包的份额记录 - 回退记录
                    RedEnvelopesShare redEnvelopesShare = new RedEnvelopesShare();
                    redEnvelopesShare.setRedId(redId);//红包id
                    redEnvelopesShare.setGetUid(redEnvelopes.getSendUid());//领取者
                    redEnvelopesShare.setGetCouponNumber(details.size());//券数量
                    redEnvelopesShare.setStatus(1);//回退记录
                    redEnvelopesShareService.save(redEnvelopesShare);


                    //将红包明细回退给发送者
                    List<CouponUser> couponUsers = new ArrayList<>(details.size());
                    for (RedEnvelopesDetails detail : details) {
                        CouponUser couponUser = new CouponUser();
                        couponUser.setUId(redEnvelopes.getSendUid());//发送者id
                        couponUser.setCpId(detail.getCpId());//券id
                        couponUser.setNumber(1);//券的数量
                        couponUser.setStatus(0);//券可用

                        couponUsers.add(couponUser);
                    }
                    //批量插入
                    couponUserService.insertBatch(couponUsers);

                    return 1;
                });

        return result;
    }

    public static void main(String[] args) {
        
        Integer number = 10000;
        Integer count = 9;

        Integer robNumber = 0;

        for (int i = 1; i <= 9; i++) {

            if (count == 1) {
                robNumber = number;
            } else {
                robNumber = (int)(Math.random() * (number / count * 2)) + 1;
            }

            System.out.println("第" + i + "个人抢到的红包数量：" + robNumber);
            //
            number -= robNumber;
            count--;
        }
    }
}
